package com.exam.api.common.utils;

import java.util.*;

/**
 * @Author bcheng
 * @Create 2023/9/16 20:30
 * @Description RecommendationSystem
 */
public class RecommendationSystem {
    // 存储用户对物品的评分数据
    private final Map<String, Map<String, Double>> userItemRatings;
    // 存储物品被哪些用户评分的数据（用于ItemCF）
    private final Map<String, Map<String, Double>> itemUserRatings;

    public RecommendationSystem() {
        userItemRatings = new HashMap<>();
        itemUserRatings = new HashMap<>();
    }

    // 添加用户对物品的评分
    public void addUserItemRating(String userId, String itemId, double rating) {
        // 将评分数据添加到用户-物品评分表
        userItemRatings.computeIfAbsent(userId, k -> new HashMap<>()).put(itemId, rating);
        // 将评分数据添加到物品-用户评分表（用于ItemCF）
        itemUserRatings.computeIfAbsent(itemId, k -> new HashMap<>()).put(userId, rating);
    }

    // 计算用户之间的相似性（UserCF）
    public double userSimilarity(String user1, String user2) {
        Map<String, Double> ratings1 = userItemRatings.get(user1);
        Map<String, Double> ratings2 = userItemRatings.get(user2);

        // 计算用户的共同评分物品
        Set<String> commonItems = new HashSet<>(ratings1.keySet());
        commonItems.retainAll(ratings2.keySet());

        if (commonItems.isEmpty()) {
            return 0.0; // 没有共同评分物品，相似度为0
        }

        // 计算皮尔逊相关系数
        double sumXY = 0.0;
        double sumX = 0.0;
        double sumY = 0.0;
        double sumX2 = 0.0;
        double sumY2 = 0.0;

        for (String item : commonItems) {
            double rating1 = ratings1.get(item);
            double rating2 = ratings2.get(item);

            sumXY += rating1 * rating2;
            sumX += rating1;
            sumY += rating2;
            sumX2 += rating1 * rating1;
            sumY2 += rating2 * rating2;
        }

        int n = commonItems.size();
        double denominator = Math.sqrt((sumX2 - (sumX * sumX) / n) * (sumY2 - (sumY * sumY) / n));

        if (denominator == 0) {
            return 0.0; // 避免除以零，相似度为0
        }

        return (sumXY - (sumX * sumY) / n) / denominator;
    }

    // 基于用户的协同过滤推荐
    public List<String> userBasedRecommendations(String userId, int numRecommendations) {
        Map<String, Double> userRatings = userItemRatings.get(userId);
        if (userRatings == null) {
            return Collections.emptyList(); // 用户不存在
        }

        Set<String> allItems = new HashSet<>(itemUserRatings.keySet());
        allItems.removeAll(userRatings.keySet()); // 排除用户已评分的物品

        List<Recommendation> recommendations = new ArrayList<>();

        for (String item : allItems) {
            double weightedSum = 0.0;
            double similaritySum = 0.0;

            for (String otherUser : userItemRatings.keySet()) {
                if (!otherUser.equals(userId)) {
                    double similarity = userSimilarity(userId, otherUser);
                    Double rating = userItemRatings.get(otherUser).get(item);

                    if (rating != null) {
                        weightedSum += similarity * rating;
                        similaritySum += Math.abs(similarity);
                    }
                }
            }

            if (similaritySum > 0) {
                double predictedRating = weightedSum / similaritySum;
                recommendations.add(new Recommendation(item, predictedRating));
            }
        }

        recommendations.sort(Collections.reverseOrder()); // 按预测评分降序排序

        List<String> topRecommendations = new ArrayList<>();
        for (int i = 0; i < Math.min(numRecommendations, recommendations.size()); i++) {
            topRecommendations.add(recommendations.get(i).item());
        }

        return topRecommendations;
    }

    // 计算物品之间的相似性（ItemCF）
    public double itemSimilarity(String item1, String item2) {
        Map<String, Double> users1 = itemUserRatings.get(item1);
        Map<String, Double> users2 = itemUserRatings.get(item2);

        // 计算共同评分的用户
        Set<String> commonUsers = new HashSet<>(users1.keySet());
        commonUsers.retainAll(users2.keySet());

        if (commonUsers.isEmpty()) {
            return 0.0; // 没有共同评分的用户，相似度为0
        }

        // 计算余弦相似度
        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;

        for (String user : commonUsers) {
            double rating1 = users1.get(user);
            double rating2 = users2.get(user);

            dotProduct += rating1 * rating2;
            norm1 += rating1 * rating1;
            norm2 += rating2 * rating2;
        }

        double denominator = Math.sqrt(norm1) * Math.sqrt(norm2);

        if (denominator == 0) {
            return 0.0; // 避免除以零，相似度为0
        }

        return dotProduct / denominator;
    }

    // 基于物品的协同过滤推荐
    public List<String> itemBasedRecommendations(String userId, int numRecommendations) {
        Map<String, Double> userRatings = userItemRatings.get(userId);
        if (userRatings == null) {
            return Collections.emptyList(); // 用户不存在
        }

        Set<String> allItems = new HashSet<>(itemUserRatings.keySet());
        allItems.removeAll(userRatings.keySet()); // 排除用户已评分的物品

        List<Recommendation> recommendations = new ArrayList<>();

        for (String item : allItems) {
            double weightedSum = 0.0;
            double similaritySum = 0.0;

            for (String ratedItem : userRatings.keySet()) {
                double similarity = itemSimilarity(item, ratedItem);
                Double rating = userRatings.get(ratedItem);

                if (rating != null) {
                    weightedSum += similarity * rating;
                    similaritySum += Math.abs(similarity);
                }
            }

            if (similaritySum > 0) {
                double predictedRating = weightedSum / similaritySum;
                recommendations.add(new Recommendation(item, predictedRating));
            }
        }

        recommendations.sort(Collections.reverseOrder()); // 按预测评分降序排序

        List<String> topRecommendations = new ArrayList<>();
        for (int i = 0; i < Math.min(numRecommendations, recommendations.size()); i++) {
            topRecommendations.add(recommendations.get(i).item());
        }

        return topRecommendations;
    }

    // 内部类用于存储推荐结果
    private record Recommendation(String item, double score) implements Comparable<Recommendation> {

        @Override
        public int compareTo(Recommendation other) {
            return Double.compare(score, other.score);
        }
    }
}
